import logging

from typing import List, Tuple,  Dict
from config.config import MatchType
from matching.single_match import find_best_match_for_target
from config.log_config import get_logger
from version_match.get_cross_version_diff import extract_cross_version_diff

SIMILARITY_THRESHOLD_ADD_DELETE = 0.55  # 可调
from utils.mongo_utils import *

logger = get_logger()
client = MongoClient("mongodb://localhost:27017/")
db = client["LocalArkTSTest"]
version_diff_collection = db["version_diff_function_info_testv2"]
npm_functions_collection = db["npm_functions_testv2"]
npm_functions = npm_functions_collection
arkts_functions_collection = db["arkts_functions_test"]
library_info_collection = db["library_info_testv2"]

# 全局变量用于记录比较详情
COMPARISON_DETAILS = {
    'library': '',
    'arkts_library': '',
    'version_list': [],
    'adjacent_comparisons': [],  # 相邻版本比较结果
    'cross_comparisons': [],  # 跨版本比较结果
    'final_result': [],
    'decision_path': [],  # 决策路径
    'local_winners': [],  # 局部胜者
    'victory_chains': []  # 胜利链
}

# 缓存函数匹配结果以提高跨版本比较速度
MATCH_CACHE = {}

def get_function_id(library: str, version: str, function_key: str) -> str:
    """从 npm_functions 集合中查询函数 _id"""
    docs = npm_functions.find({
        "library": library,
        "unique_key": function_key
    })

    if not docs:
        raise ValueError(f"Function not found: {library}@{version}#{function_key}")
    for doc in docs:
        if version in doc['version']:
            str_id = str(doc['_id'])
            return str_id
    return None


def evaluate_diff_function(
        library: str,
        from_version: str,
        to_version: str,
        diff_item: dict,
        arkts_library: str
) -> str:
    """
    评估单个diff函数，返回 "from" 或 "to"
    使用缓存提高性能
    """
    diff_type = diff_item["type"]

    # 生成缓存键
    cache_key = f"{library}:{arkts_library}:{diff_item.get('function_key', '')}:{diff_item.get('from_function_key', '')}"

    if diff_type == "add":
        func_key = diff_item["function_key"]
        func_id = str(get_function_id(library, to_version, func_key))

        # 检查缓存
        if cache_key in MATCH_CACHE:
            best_match = MATCH_CACHE[cache_key]
        else:
            results = find_best_match_for_target(
                func_id,
                MatchType.TPL_VERSION_MATCH,
                ArkTS_Library=arkts_library
            )
            if results is None:
                MATCH_CACHE[cache_key] = None
                return "from"
            best_match, _ = results
            MATCH_CACHE[cache_key] = best_match

        if best_match and best_match.vector_similarity >= SIMILARITY_THRESHOLD_ADD_DELETE:
            return "to"
        else:
            return "from"

    elif diff_type == "delete":
        func_key = diff_item["function_key"]
        func_id = get_function_id(library, from_version, func_key)

        # 检查缓存
        if cache_key in MATCH_CACHE:
            best_match = MATCH_CACHE[cache_key]
        else:
            results = find_best_match_for_target(
                func_id,
                MatchType.TPL_VERSION_MATCH,
                ArkTS_Library=arkts_library
            )
            if results is None:
                MATCH_CACHE[cache_key] = None
                return "to"
            best_match, _ = results
            MATCH_CACHE[cache_key] = best_match

        if best_match and best_match.vector_similarity < SIMILARITY_THRESHOLD_ADD_DELETE:
            return "to"
        else:
            return "from"

    elif diff_type == "update":
        from_func_key = diff_item["from_function_key"]
        to_func_key = diff_item["function_key"]

        from_cache_key = f"{library}:{arkts_library}:{from_func_key}:"
        to_cache_key = f"{library}:{arkts_library}:{to_func_key}:"

        # 检查缓存
        if from_cache_key in MATCH_CACHE:
            from_best_match = MATCH_CACHE[from_cache_key]
        else:
            from_func_id = get_function_id(library, from_version, from_func_key)
            from_results = find_best_match_for_target(
                from_func_id,
                MatchType.TPL_VERSION_MATCH,
                ArkTS_Library=arkts_library
            )
            if from_results is None:
                from_best_match = None
            else:
                from_best_match, _ = from_results
            MATCH_CACHE[from_cache_key] = from_best_match

        if to_cache_key in MATCH_CACHE:
            to_best_match = MATCH_CACHE[to_cache_key]
        else:
            to_func_id = get_function_id(library, to_version, to_func_key)
            to_results = find_best_match_for_target(
                to_func_id,
                MatchType.TPL_VERSION_MATCH,
                ArkTS_Library=arkts_library
            )
            if to_results is None:
                to_best_match = None
            else:
                to_best_match, _ = to_results
            MATCH_CACHE[to_cache_key] = to_best_match

        if from_best_match is None:
            return "to"
        if to_best_match is None:
            return "from"

        if to_best_match.vector_similarity > from_best_match.vector_similarity:
            return "to"
        else:
            return "from"

    else:
        raise ValueError(f"Unknown diff type: {diff_type}")


def compare_two_versions(
        library: str,
        from_version: str,
        to_version: str,
        arkts_library: str,
        is_cross_version: bool = False
) -> Tuple[str, Dict]:
    """
    比较两个版本，返回胜者和投票详情
    """
    # 如果是跨版本比较，使用新的diff提取函数
    if is_cross_version:
        diff_functions = extract_cross_version_diff(library, from_version, to_version)

        if not diff_functions:
            logger.warning(f"无法提取跨版本diff: {from_version} 和 {to_version}")
            # 返回一个包含所有必要字段的字典
            comparison_result = {
                'from_version': from_version,
                'to_version': to_version,
                'votes': {from_version: 0, to_version: 0},
                'winner': from_version,  # 默认返回from_version
                'vote_details': [],
                'is_cross_version': is_cross_version,
                'error': 'No diff found'
            }
            return from_version, comparison_result
    else:
        # 相邻版本比较
        diff_doc = version_diff_collection.find_one({
            "library": library,
            "from_version": from_version,
            "version": to_version
        })
        diff_functions = diff_doc.get("diff_function", []) if diff_doc else []

    if not diff_functions:
        logger.info(f"没有diff函数: {library} {from_version} -> {to_version}")
        # 返回一个包含所有必要字段的字典
        comparison_result = {
            'from_version': from_version,
            'to_version': to_version,
            'votes': {from_version: 0, to_version: 0},
            'winner': to_version,  # 没有diff时默认返回to_version
            'vote_details': [],
            'is_cross_version': is_cross_version,
            'no_diff': True
        }
        return to_version, comparison_result

    # 初始化投票
    votes = {from_version: 0, to_version: 0}
    vote_details = []

    # 对每个diff函数投票
    for diff_item in diff_functions:
        try:
            support_side = evaluate_diff_function(
                library, from_version, to_version, diff_item, arkts_library
            )

            if support_side == "from":
                votes[from_version] += 1
            else:
                votes[to_version] += 1

            vote_details.append({
                'diff_type': diff_item['type'],
                'function_key': diff_item.get('function_key', diff_item.get('from_function_key', 'N/A')),
                'support_side': support_side,
                'votes_after': votes.copy()
            })

        except Exception as e:
            logger.warning(f"Warning: failed to evaluate diff item {diff_item}: {e}")
            continue

    # 确定胜者
    winner = from_version if votes[from_version] > votes[to_version] else to_version

    comparison_result = {
        'from_version': from_version,
        'to_version': to_version,
        'votes': votes,
        'winner': winner,
        'vote_details': vote_details,
        'is_cross_version': is_cross_version
    }

    return winner, comparison_result


def find_local_winners(version_list: List[str], adjacent_winners: Dict[Tuple[str, str], str]) -> List[str]:
    """
    找出所有局部胜者（没有其他版本能通过传递性战胜它的版本）
    """
    # 构建胜利图
    victory_graph = {}
    for (from_v, to_v), winner in adjacent_winners.items():
        if winner not in victory_graph:
            victory_graph[winner] = []
        if winner == from_v:
            victory_graph[winner].append(to_v)
        else:
            victory_graph[winner].append(from_v)

    # 找出所有没有被任何其他版本战胜的版本
    all_versions = set(version_list)
    defeated_versions = set()
    for winner, defeated_list in victory_graph.items():
        defeated_versions.update(defeated_list)

    local_winners = list(all_versions - defeated_versions)

    # 如果没有局部胜者（可能所有版本都被战胜了），返回最新版本
    if not local_winners:
        local_winners = [version_list[-1]]

    return local_winners


def build_victory_chains(version_list: List[str], adjacent_winners: Dict[Tuple[str, str], str]) -> List[List[str]]:
    """
    构建胜利链，展示传递关系
    """
    chains = []
    visited = set()

    def build_chain(start_version: str, current_chain: List[str]):
        if start_version in visited:
            return
        visited.add(start_version)
        current_chain.append(start_version)

        # 找出被start_version战胜的版本
        defeated = []
        for (from_v, to_v), winner in adjacent_winners.items():
            if winner == start_version:
                defeated.append(to_v if winner == from_v else from_v)

        # 如果没有战胜任何版本，这是一个链的终点
        if not defeated:
            chains.append(current_chain.copy())
        else:
            # 继续构建链
            for next_version in defeated:
                build_chain(next_version, current_chain.copy())

    # 从每个可能的起点开始构建链
    for version in version_list:
        if version not in visited:
            build_chain(version, [])

    return chains


def find_closest_npm_versions(
        library: str,
        arkts_library: str
) -> List[str]:
    """
    使用传递性判断和跨版本比较找到最接近的版本
    支持平局处理，返回多个平局的版本
    """
    # 重置全局变量
    global COMPARISON_DETAILS, MATCH_CACHE
    COMPARISON_DETAILS = {
        'library': library,
        'arkts_library': arkts_library,
        'version_list': [],
        'adjacent_comparisons': [],
        'cross_comparisons': [],
        'final_result': [],
        'decision_path': [],
        'local_winners': [],
        'victory_chains': []
    }
    MATCH_CACHE = {}  # 清空缓存

    # 获取版本列表
    results = library_info_collection.find({"library": library})
    if not results:
        logger.error(f"Failed to find library info for {library}")
        return []

    version_docs = results[0]
    version_items = version_docs["versionInfo"]
    version_list = [item["version"] for item in version_items]
    COMPARISON_DETAILS['version_list'] = version_list

    logger.info(f"处理: {arkts_library}, {library}")
    logger.info(f"版本列表: {version_list}")

    if len(version_list) == 1:
        COMPARISON_DETAILS['final_result'] = version_list
        COMPARISON_DETAILS['decision_path'].append("只有一个版本，直接返回")
        return version_list

    # 第一步：比较所有相邻版本
    adjacent_winners = {}  # 记录相邻比较的胜者
    for i in range(len(version_list) - 1):
        from_v = version_list[i]
        to_v = version_list[i + 1]

        winner, comparison_result = compare_two_versions(library, from_v, to_v, arkts_library, False)

        COMPARISON_DETAILS['adjacent_comparisons'].append(comparison_result)

        # 处理平局情况
        if winner == 'tie':
            logger.info(f"  结果: 平局")
            COMPARISON_DETAILS['decision_path'].append(f"相邻比较 {from_v} vs {to_v}: 平局")
        else:
            adjacent_winners[(from_v, to_v)] = winner
            COMPARISON_DETAILS['decision_path'].append(f"相邻比较 {from_v} vs {to_v}: 胜者 {winner}")

    # 第二步：找出局部胜者
    local_winners = find_local_winners(version_list, adjacent_winners)
    COMPARISON_DETAILS['local_winners'] = local_winners

    logger.info(f"局部胜者: {local_winners}")
    COMPARISON_DETAILS['decision_path'].append(f"找出局部胜者: {local_winners}")

    # 第三步：构建胜利链
    victory_chains = build_victory_chains(version_list, adjacent_winners)
    COMPARISON_DETAILS['victory_chains'] = victory_chains

    logger.info(f"胜利链: {victory_chains}")
    COMPARISON_DETAILS['decision_path'].append(f"胜利链: {victory_chains}")

    # 如果只有一个局部胜者，直接返回
    if len(local_winners) == 1:
        COMPARISON_DETAILS['final_result'] = local_winners
        COMPARISON_DETAILS['decision_path'].append("只有一个局部胜者，直接返回")
        return local_winners

    # 第四步：如果有多个局部胜者，进行跨版本比较（终极对决）
    logger.info(f"发现 {len(local_winners)} 个局部胜者，需要进行终极对决")
    COMPARISON_DETAILS['decision_path'].append(f"多个局部胜者，进行跨版本比较")

    # 构建胜负关系图
    victory_graph = {v: {'wins': 0, 'losses': 0, 'ties': set()} for v in local_winners}

    # 两两比较局部胜者
    for i in range(len(local_winners)):
        for j in range(i + 1, len(local_winners)):
            v1 = local_winners[i]
            v2 = local_winners[j]

            logger.info(f"跨版本比较（终极对决）: {v1} vs {v2}")
            winner, comparison_result = compare_two_versions(library, v1, v2, arkts_library, True)

            COMPARISON_DETAILS['cross_comparisons'].append(comparison_result)

            if winner == 'tie':
                logger.info(f"  结果: 平局")
                COMPARISON_DETAILS['decision_path'].append(f"跨版本比较 {v1} vs {v2}: 平局")
                # 记录平局关系
                victory_graph[v1]['ties'].add(v2)
                victory_graph[v2]['ties'].add(v1)
            elif winner == v1:
                logger.info(f"  胜者: {v1}")
                COMPARISON_DETAILS['decision_path'].append(f"跨版本比较 {v1} vs {v2}: 胜者 {v1}")
                victory_graph[v1]['wins'] += 1
                victory_graph[v2]['losses'] += 1
            else:
                logger.info(f"  胜者: {v2}")
                COMPARISON_DETAILS['decision_path'].append(f"跨版本比较 {v1} vs {v2}: 胜者 {v2}")
                victory_graph[v2]['wins'] += 1
                victory_graph[v1]['losses'] += 1

    # 分析胜负关系，找出最终胜者
    logger.info("分析胜负关系:")
    for v, stats in victory_graph.items():
        logger.info(f"  {v}: 胜{stats['wins']} 负{stats['losses']} 平{len(stats['ties'])}")

    # 找出没有被任何版本明确战胜的版本（没有losses或losses最少的）
    min_losses = min(stats['losses'] for stats in victory_graph.values())
    undefeated_versions = [v for v, stats in victory_graph.items() if stats['losses'] == min_losses]

    logger.info(f"没有被明确战胜的版本（最少失败次数: {min_losses}）: {undefeated_versions}")

    # 如果只有一个 undefeated 版本，直接返回
    if len(undefeated_versions) == 1:
        final_winners = undefeated_versions
        COMPARISON_DETAILS['decision_path'].append(f"唯一 undefeated 版本: {final_winners}")
    else:
        # 如果有多个 undefeated 版本，检查它们之间的胜负关系
        # 找出平局组
        tie_groups = []
        visited = set()

        for version in undefeated_versions:
            if version in visited:
                continue

            # 找出与该版本平局的所有版本
            tie_group = {version}
            queue = [version]

            while queue:
                current = queue.pop(0)
                for tied_version in victory_graph[current]['ties']:
                    if tied_version in undefeated_versions and tied_version not in visited:
                        tie_group.add(tied_version)
                        visited.add(tied_version)
                        queue.append(tied_version)

            tie_groups.append(list(tie_group))
            visited.update(tie_group)

        logger.info(f"平局组: {tie_groups}")

        # 选择最大的平局组（或者所有组）
        if len(tie_groups) == 1:
            final_winners = tie_groups[0]
            COMPARISON_DETAILS['decision_path'].append(f"所有 undefeated 版本互相平局: {final_winners}")
        else:
            # 如果有多个平局组，选择胜场最多的组
            max_wins = -1
            best_group = []

            for group in tie_groups:
                group_wins = sum(victory_graph[v]['wins'] for v in group)
                if group_wins > max_wins:
                    max_wins = group_wins
                    best_group = group

            final_winners = best_group
            COMPARISON_DETAILS['decision_path'].append(f"选择胜场最多的平局组: {final_winners} (胜场: {max_wins})")

    COMPARISON_DETAILS['final_result'] = final_winners
    logger.info(f"最终胜者: {final_winners}")
    COMPARISON_DETAILS['decision_path'].append(f"最终胜者: {final_winners}")

    return final_winners


def print_comparison_details():
    """只打印局部胜者信息"""
    print("\n" + "=" * 60)
    print(f"局部胜者 - 库: {COMPARISON_DETAILS['library']}, ArkTS库: {COMPARISON_DETAILS['arkts_library']}")
    print("=" * 60)

    print(f"\n局部胜者: {COMPARISON_DETAILS['local_winners']}")

    # 检查跨版本比较的平局情况
    if COMPARISON_DETAILS['cross_comparisons']:
        tie_comparisons = []
        for comp in COMPARISON_DETAILS['cross_comparisons']:
            if comp.get('winner') == 'tie':
                tie_comparisons.append(f"{comp['from_version']} vs {comp['to_version']}")

        if tie_comparisons:
            print(f"\n终极对决中的平局: {', '.join(tie_comparisons)}")

    if len(COMPARISON_DETAILS['final_result']) > 1:
        print(f"\n最终结果: {COMPARISON_DETAILS['final_result']} (平局)")
    else:
        print(f"\n最终结果: {COMPARISON_DETAILS['final_result']}")

    print("=" * 60 + "\n")


def compare_cross_versions(
        library: str,
        from_version: str,
        to_version: str,
        arkts_library: str
) -> Tuple[str, Dict]:
    """
    直接比较两个跨版本，使用diff提取功能

    Returns:
        (winner, comparison_details)
    """
    logger.info(f"开始跨版本比较: {library} {from_version} vs {to_version}")

    # 提取跨版本diff
    diff_functions = extract_cross_version_diff(library, from_version, to_version)

    if not diff_functions:
        logger.warning(f"无法提取跨版本diff: {from_version} 和 {to_version}")
        comparison_result = {
            'from_version': from_version,
            'to_version': to_version,
            'votes': {from_version: 0, to_version: 0},
            'winner': from_version,
            'vote_details': [],
            'is_cross_version': True,
            'error': 'No diff found'
        }
        return from_version, comparison_result

    # 初始化投票
    votes = {from_version: 0, to_version: 0}
    vote_details = []

    logger.info(f"开始对 {len(diff_functions)} 个跨版本diff函数进行投票")

    # 对每个diff函数投票
    for i, diff_item in enumerate(diff_functions, 1):
        try:
            support_side = evaluate_diff_function(
                library, from_version, to_version, diff_item, arkts_library
            )

            if support_side == "from":
                votes[from_version] += 1
            else:
                votes[to_version] += 1

            vote_details.append({
                'diff_index': i,
                'diff_type': diff_item['type'],
                'function_key': diff_item.get('function_key', diff_item.get('from_function_key', 'N/A')),
                'support_side': support_side,
                'votes_after': votes.copy()
            })

            logger.debug(f"  Diff {i}: {diff_item['type']} - {diff_item.get('function_key', 'N/A')} -> {support_side}")

        except Exception as e:
            logger.warning(f"Warning: failed to evaluate diff item {diff_item}: {e}")
            continue

    # 确定胜者
    winner = from_version if votes[from_version] > votes[to_version] else to_version

    comparison_result = {
        'from_version': from_version,
        'to_version': to_version,
        'votes': votes,
        'winner': winner,
        'vote_details': vote_details,
        'is_cross_version': True,
        'diff_count': len(diff_functions)
    }

    logger.info(f"跨版本比较结果: {winner} (票数: {votes})")

    return winner, comparison_result


if __name__ == '__main__':
    # 设置日志
    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger(__name__)

    # 指定要处理的库
    TARGET_LIBRARY = "base64-js"  # 修改为你想要测试的库名
    TARGET_ARKTS_LIBRARY = "base64"  # 修改为对应的ArkTS库名

    logger.info(f"开始处理指定库: {TARGET_LIBRARY} -> {TARGET_ARKTS_LIBRARY}")

    try:
        # 调用函数获取最接近的版本
        result_version = find_closest_npm_versions(TARGET_LIBRARY, TARGET_ARKTS_LIBRARY)

        # 只打印局部胜者信息
        print_comparison_details()

        logger.info(f"最终结果: {result_version}")

    except Exception as e:
        logger.error(f"处理库 {TARGET_LIBRARY} 时出错: {str(e)}")
        import traceback

        traceback.print_exc()
